﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace tamzarian
{
    /// <summary>
    /// Quaternion w + i*x + j*y + k*z
    /// where i,j,k are the Quaternion units.
    /// </summary>
    public class Quaternion
    {
        public readonly double W;
        public readonly double X;
        public readonly double Y;
        public readonly double Z;

        public Quaternion(double w, double x, double y, double z)
        {
            this.W = w;
            this.X = x;
            this.Y = y;
            this.Z = z;
        }

        #region Override Object Methods

        public override string ToString()
        {
            return string.Format("Quaternion({0}, {1}, {2}, {3})",
                W, X, Y, Z);
        }

        public override int GetHashCode()
        {
            return W.GetHashCode() ^
                   X.GetHashCode() ^
                   Y.GetHashCode() ^
                   Z.GetHashCode();
        }

        public override bool Equals(object other)
        {
            return other is Quaternion && Equals((Quaternion)other);
        }

        public override bool Equals(Quaternion other)
        {
            return W == other.W &&
                   X == other.X &&
                   Y == other.Y &&
                   Z == other.Z;
        }

        public static bool operator ==(Double lhs, Double rhs)
        {
            return lhs.Equals(rhs);
        }

        public static bool operator !=(Double lhs, Double rhs)
        {
            return !lhs.Equals(rhs);
        }

        #endregion

        #region Arithmetic

        public static Quaternion operator +(Quaternion lhs, Quaternion rhs)
        {
            return new Quaternion(lhs.W + rhs.W,
                                  lhs.X + rhs.X,
                                  lhs.Y + rhs.Y,
                                  lhs.Z + rhs.Z);
        }

        public static Quaternion operator *(Quaternion lhs, Quaternion rhs)
        {
            return new Quaternion(lhs.W * rhs.W - lhs.X * rhs.X - lhs.Y * rhs.Y - lhs.Z * rhs.Z,
                                  lhs.W * rhs.X + lhs.X * rhs.W + lhs.Y * rhs.Z - lhs.Z * rhs.Y,
                                  lhs.W * rhs.Y - lhs.X * rhs.Z + lhs.Y * rhs.W + lhs.Z * rhs.X,
                                  lhs.W * rhs.Z + lhs.X * rhs.Y - lhs.Y * rhs.X + lhs.Z * rhs.W);
        }

        public Quaternion Conjugate()
        {
            return new Quaternion(W, -X, -Y, -Z);
        }

        public Quaternion Reciprocal()
        {
            double n = NormSqr();
            return new Quaternion(W / n, -X / n, -Y / n, -Z / n);
        }

        public double Norm()
        {
            return Math.Sqrt(W * W + X * X + Y * Y + Z * Z);
        }

        public double NormSqr()
        {
            return W * W + X * X + Y * Y + Z * Z;
        }

        public Quaternion Unit()
        {
            double n = Norm();
            return new Quaternion(W / n, X / n, Y / n, Z / n);
        }

        #endregion
    }
}
